<?php

/*
Copyright 2009-2011 Sam Weiss
All Rights Reserved.

This file is part of Spark/Plug.

Spark/Plug is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

if (!defined('spark/plug'))
{
	header('HTTP/1.1 403 Forbidden');
	exit('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>403 Forbidden</title></head><body><h1>Forbidden</h1><p>You don\'t have permission to access the requested resource on this server.</p></body></html>');
}

// -----------------------------------------------------------------------------

class _SparkAuthModel extends SparkModel
{
	private $_sessionKey;
	private $_hasher;
	
	// --------------------------------------------------------------------------

	public function __construct()
	{
		parent::__construct();
		
		$this->_sessionKey = 'auth_session';
		try
		{
			$this->_hasher = $this->factory->manufacture('SparkHasher');
		}
		catch (Exception $e)
		{
			$this->_hasher = NULL;
		}
	}

	//----------------------------------------------------------------

	public function encryptPassword($pw)
	{
		return $this->_hasher ? $this->_hasher->hash($pw) : sha1($pw);
	}

	//----------------------------------------------------------------

	public function checkPassword($pw, $hash)
	{
		return $this->_hasher ? $this->_hasher->validate($pw, $hash) : (sha1($pw) === $hash);
	}

	// --------------------------------------------------------------------------

	public function authenticate($updateSession = true)
	{
		static $auth = NULL;		// prevent duplication of work within a single page request
		
		if (!isset($auth))
		{
			$auth = $this->session->get($this->_sessionKey);
			
			if (empty($auth))
			{
				return ($auth = false);
			}
			
			if ($this->isExpired($auth))
			{
				$this->session->remove($this->_sessionKey);
				return ($auth = false);
			}
			
			// update last access time
			
			$auth['last_access'] = time();
			
			if ($updateSession)
			{
				// update session
			
				$this->session->set($this->_sessionKey, $auth);
			}
		}
		
		return $auth;
	}

	// --------------------------------------------------------------------------

	public function login(&$userInfo)
	{
		// load auth config
		
		$config = $this->config->get('auth');
		
		// load the database
		
		$db = $this->loadDB(isset($config['database']) ? $config['database'] : NULL);
		
		// load the user

		$userFields =& $config['user_fields'];
		$bind[] = $userInfo['login'];
		
		// fetch user row if login exists
		
		if (!$row = $db->selectRow($config['user_table'], '*', $db->quoteIdentifier($userFields['login']).'=?', $bind))
		{
			return false;
		}

		// check password

		if (!$this->checkPassword($userInfo['password'], $row[$userFields['password']]))
		{
			return false;
		}

		$auth['id'] = $row[$userFields['id']];
		$auth['login'] = $row[$userFields['login']];
		$auth['email'] = $row[$userFields['email']];
		$auth['logged'] = $row[$userFields['logged']];
		$auth['timeout'] = $config['timeout'];
		$auth['last_access'] = time();
		$auth['ip_address'] = SparkUtil::remote_ip();
		$auth['nonce'] = '';

		// an empty nonce column in the database means we don't enforce nonce for this user

		if (!empty($userFields['nonce']) && !empty($row[$userFields['nonce']]))
		{
			$auth['nonce'] = SparkUtil::make_nonce();
		}
		
		// update session
		
		$this->session->set($this->_sessionKey, $auth);
		
		// update last login time in DB
		
		$row = array
		(
			$userFields['logged'] => $db->getFunction('date')->now(),
		);
		
		// update authentication nonce (optional)
		
		if (!empty($auth['nonce']))
		{
			$row[$userFields['nonce']] = md5($auth['nonce']);
		}
		
		$db->updateRows($config['user_table'], $row, $db->quoteIdentifier($userFields['id']).'=?', $auth['id']);
		
		$userInfo = $auth;
		return true;
	}

	// --------------------------------------------------------------------

	public function logout(&$userInfo)
	{
		$userInfo = $this->session->get($this->_sessionKey);

		if ($userInfo !== NULL)
		{
			$this->session->remove($this->_sessionKey);
			return true;
		}
		return false;
	}

	// --------------------------------------------------------------------

	public function loggedIn()
	{
		return ($this->authenticate(false) !== false);
	}

	// --------------------------------------------------------------------

	private function isExpired(&$auth)
	{
		if (($auth['timeout'] > 0) && ((time() - $auth['last_access']) > $auth['timeout']))
		{
			return true;
		}
		
		$config = $this->config->get('auth');
		$userFields =& $config['user_fields'];

		if (!empty($userFields['nonce']) && !empty($auth['id']))
		{			
			$db = $this->loadDB(isset($config['database']) ? $config['database'] : NULL);
			if (!$row = $db->selectRow($config['user_table'], $db->quoteIdentifier($userFields['nonce']), $db->quoteIdentifier($userFields['id']).'=?', $auth['id']))
			{
				return true;
			}

			// an empty nonce column in the database means we don't enforce nonce for this user

			$nonce = $row[$userFields['nonce']];
			return !empty($nonce) && (md5($auth['nonce']) !== $nonce);
		}
	
		return false;
	}

	// --------------------------------------------------------------------------
}
